/*
 * Copyright 2012 Gregor Schauer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package at.schauer.gregor.vienna.convert.vienna.util;

import at.schauer.gregor.vienna.convert.vienna.base.AbstractContainerConverter;
import at.schauer.gregor.vienna.serializer.Serializer;
import at.schauer.gregor.vienna.util.ViennaUtils;
import org.springframework.util.ClassUtils;

import java.util.*;

/**
 * @author Gregor Schauer
 */
public class MapConverter<T extends Map<K, V>, K, V> extends AbstractContainerConverter<T, Map.Entry<K, V>> {
	private static final String EQUALS = "=";

	public MapConverter() {
		super("{", "}");
	}

	@Override
	public T sourceToTarget(String source, Class<? extends T> targetClass) {
		source = stripContainerSequences(source);
		String[] split = split(source, String.format("[%s%s]", getSeparator(), EQUALS));
		T instance = createInstance(targetClass, split.length);

		for (int i = 0; i < split.length; i++) {
			add(instance, i, new AbstractMap.SimpleEntry<K, V>(this.<K>deserialize(split[i]), this.<V>deserialize(split[++i])));
		}
		return instance;
	}

	@Override
	public String targetToSource(T target) {
		StringBuilder builder = new StringBuilder();
		appendLeadingSequence(builder);

		boolean required = false;
		List<Integer> separatorPositions = new ArrayList<Integer>();
		for (Iterator<Map.Entry<K, V>> iterator = getElements(target); iterator.hasNext(); ) {
			Map.Entry<K, V> elem = iterator.next();

			// Key
			Object identifier = ViennaUtils.SerializationUtils.save(ctx, elem.getKey());
			String str = ClassUtils.getQualifiedName(elem.getKey().getClass()) + Serializer.CLASS_OBJECT_SEPARATOR + identifier;
			builder.append(str).append(EQUALS);

			separatorPositions.add(str.length());
			required = required || requireIndices(str);

			// Value
			identifier = ViennaUtils.SerializationUtils.save(ctx, elem.getValue());
			str = ClassUtils.getQualifiedName(elem.getValue().getClass()) + Serializer.CLASS_OBJECT_SEPARATOR + identifier;
			builder.append(str).append(getSeparator());

			separatorPositions.add(str.length());
			required = required || requireIndices(str);
		}

		if (separatorPositions.size() > 1 && required) {
			builder.insert(0, separatorPositions.toString().replace(" ", ""));
		}
		return appendTrailingSequence(builder).toString();
	}

	@Override
	protected void add(T instance, int i, Map.Entry<K, V> elem) {
		instance.put(elem.getKey(), elem.getValue());
	}

	@Override
	protected boolean requireIndices(String str) {
		return super.requireIndices(str) || str.contains(EQUALS);
	}

	@Override
	protected Iterator<Map.Entry<K, V>> getElements(T target) {
		return target.entrySet().iterator();
	}
}
